package client;

import java.net.Socket;

import javax.swing.JOptionPane;

import nanoxml.XMLElement;
import utils.AbstractConnectionThread;
import utils.IConnection;
import utils.ITransmittable;
import utils.ServerInfo;
import utils.Settings;
import utils.Table;
import utils.TableList;
import utils.User;
import utils.UserList;

import commands.CommandChatMessage;
import commands.CommandJoinTable;
import commands.CommandLeaveTable;
import commands.CommandNewTable;
import commands.CommandNotifyDBCrashed;
import commands.CommandOtherPlayerName;
import commands.CommandPlayerDisconnected;
import commands.CommandStatistics;
import commands.CommandUserAlreadyConnected;
import commands.CommandUserExit;
import commands.Commands;

import enums.PlayerColor;

/**
 * Client's connection to the server 
 */
public class ConnectionThreadRPSClientToServer extends AbstractConnectionThread implements IGameConnection {
	
	private ServerInfo serverInfo;
	private ClientFrame clientFrame;
	private LogonPanel logonPanel;
	
	private TableOwnerServerThread tableOwnerServerThread;
	
	
	public ConnectionThreadRPSClientToServer (Socket socket, String username, LogonPanel logonPanel ,ClientFrame clientFrame ) {
		super(socket);
		this.username = username;
		this.serverInfo = null; 
		this.clientFrame = clientFrame;
		this.logonPanel = logonPanel;
		
		tableOwnerServerThread = null;
	}
	
	
	/**
	 * Parse the command and perform the required action
	 */
	@Override
	public void parse(XMLElement command) {
		try {
			if (command.getName().equals(Commands.SERVER_INFO)) {
				serverInfo = new ServerInfo(command);
				clientFrame.connectOK(this); 
			}
			else if (command.getName().equals(Commands.USER)) {
				User user = new User (command);
				serverInfo.getUserList().addUser(user);
			}
			else if (command.getName().equals(Commands.USER_NOT_VERIFIED)) {
				JOptionPane.showMessageDialog(clientFrame,"Username/Password incorrect. Plaese insert your details again.", "Wrong Details", JOptionPane.ERROR_MESSAGE);
				logonPanel.setButtons(true);
				stopLoop();
				socket.close();
			}
			else if (command.getName().equals(Commands.NEW_USER)) {
				JOptionPane.showMessageDialog(logonPanel,"User has been added successfully.", "New user", JOptionPane.INFORMATION_MESSAGE);
				logonPanel.setButtons(true);
			}
			else if (command.getName().equals(Commands.NEW_USER_ERROR)) {
				JOptionPane.showMessageDialog(logonPanel,"Username already exists. Plaese enter new username.", "Username problem", JOptionPane.ERROR_MESSAGE);
				logonPanel.setButtons(true);
			}
			else if (command.getName().equals(Commands.STATISTICS)) {
				CommandStatistics cmdStat = new CommandStatistics(command);
				String loses = cmdStat.getLoseNum();
				String winnings = cmdStat.getWinNum();
				Integer winInteger = Integer.parseInt(winnings);
				Integer loseInteger = Integer.parseInt(loses);
				int totalGames = winInteger + loseInteger;
				Integer rate = 0;
				if (totalGames > 0) 
					rate = (winInteger*100)/totalGames;
				
				String message = "The number of wins you gathered so far is: " + winnings + "." + Settings.NEW_LINE;
				message += "The number of loses you gathered so far is: " + loses + "." + Settings.NEW_LINE;
				message += "Your success rate is : " + rate + "% (" + winInteger + "/" + totalGames + ")";
				JOptionPane.showMessageDialog(clientFrame, message, "Statistics", JOptionPane.INFORMATION_MESSAGE);	
			}
			else if (command.getName().equals(Commands.USER_EXIT)) {
				CommandUserExit cmdUserExit = new CommandUserExit(command);	
				String usernameExit = cmdUserExit.getUsername();
				
				// Each user should check if he is the owner of a table, that the user that exited was in the table
				Table tableOwnedByMe = serverInfo.getTableList().getTableByOwner(username);
				if (tableOwnedByMe != null && tableOwnedByMe.containsPlayer(usernameExit))
				{
					// Simulate this user leaving the table properly
					IConnection connToUserExit = tableOwnerServerThread.getConnections().getConnection(usernameExit);
					
					if (connToUserExit != null)
					{
						CommandLeaveTable commandLeaveTable = new CommandLeaveTable (usernameExit, tableOwnedByMe.getTableNum());
						connToUserExit.leaveTable(commandLeaveTable);
					}
				}
				// If the user that exited is an owner of a table, remove this table from the list
				Table tableOwnedUserExit = serverInfo.getTableList().getTableByOwner(usernameExit);
				
				if (tableOwnedUserExit != null)
				{
					serverInfo.getTableList().removeTable(tableOwnedUserExit.getTableNum());
				}
				
				serverInfo.getUserList().removeUser(usernameExit);
			}
			else if (command.getName().equals(Commands.NEW_TABLE)) {
				CommandNewTable cmdNewTable = new CommandNewTable(command);	
				User userCreated = serverInfo.getUserList().getUser(cmdNewTable.getUsername());
				Table table = serverInfo.getTableList().addTable(userCreated, cmdNewTable.getOwnerIP(), PlayerColor.RED, cmdNewTable.getGameSettings());
				if (username.equals(cmdNewTable.getUsername())) {
					tableOwnerServerThread = new TableOwnerServerThread(this , table , PlayerColor.RED);
					tableOwnerServerThread.start();
				}
			}
			else if (command.getName().equals(Commands.JOIN_TABLE)) {
				CommandJoinTable cmdJoinTable = new CommandJoinTable(command);	
				joinTable(cmdJoinTable);
			}
			else if (command.getName().equals(Commands.LEAVE_TABLE)) {
				CommandLeaveTable commandLeaveTable= new CommandLeaveTable(command);	
				leaveTable(commandLeaveTable);
			}
			else if (command.getName().equals(Commands.CHAT_MESSAGE)) {
				CommandChatMessage commandChatMessage= new CommandChatMessage(command);	
				clientFrame.getRpsClientPanel().receiveGameChat(commandChatMessage);
			}
			else if (command.getName().equals(Commands.NOTIFY_DB_CRASHED)) {
				CommandNotifyDBCrashed commandNotifyDBCrashed= new CommandNotifyDBCrashed(command);	
				String message = commandNotifyDBCrashed.getErrorMessage();
				String panel = commandNotifyDBCrashed.getPanel();
				if (panel.equals("logonPanel")) {
					JOptionPane.showMessageDialog(logonPanel, message, "Database unavailable", JOptionPane.ERROR_MESSAGE);
					logonPanel.setButtons(true);
				}
				else {
					JOptionPane.showMessageDialog(clientFrame, message, "Database unavailable", JOptionPane.ERROR_MESSAGE);
				}
			}
			else if (command.getName().equals(Commands.OTHER_PLAYER_NAME)) {
				CommandOtherPlayerName commandOtherPlayerName = new CommandOtherPlayerName(command);
				setOtherPlayerName(commandOtherPlayerName);
			}
			else if (command.getName().equals(Commands.PLAYER_DISCONNECTED)) {
				// Notify to all the viewers that the player disconnected
				CommandPlayerDisconnected commandPlayerDisconnected = new CommandPlayerDisconnected(command);
				String usernameDisconnected = commandPlayerDisconnected.getUsername();
				int tableNum = commandPlayerDisconnected.getTableNum();
				
				serverInfo.getTableList().removePlayer(tableNum, usernameDisconnected);

				serverInfo.getUserList().removeUser(usernameDisconnected);
				
				Table tableOfOwner = serverInfo.getTableList().getTable(tableNum);
				if (tableOfOwner != null && usernameDisconnected.equals(tableOfOwner.getOwnerUsername()))
				{
					serverInfo.getTableList().removeTable(tableNum);
				}
			}
			else if  (command.getName().equals(Commands.USER_ALREADY_CONNECTED)) {
				CommandUserAlreadyConnected commandUserAlreadyConnected = new CommandUserAlreadyConnected(command);
				String message = commandUserAlreadyConnected.getMessage();
				JOptionPane.showMessageDialog(logonPanel, message, "Connection Problems", JOptionPane.ERROR_MESSAGE);
				logonPanel.setButtons(true);
				stopLoop();
				socket.close();
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void leaveTable(CommandLeaveTable commandLeaveTable) {
		String usernameLeaveTable = commandLeaveTable.getUsername();
		int tableNum = commandLeaveTable.getTableNum();
		TableList tableList = serverInfo.getTableList();
		Table table = tableList.getTable(tableNum);
		
		tableList.removePlayer(tableNum, usernameLeaveTable);
		
		if (usernameLeaveTable.equals(table.getOwnerUsername()))
		{
			serverInfo.getTableList().removeTable(tableNum);
		}
		else if (usernameLeaveTable.equals(table.getOtherPlayerUsername()))
		{
			serverInfo.getTableList().getTable(tableNum).setOtherPlayerUsername("");
			serverInfo.getTableList().refreshObservers();
		}
	}
	
	private void setOtherPlayerName(CommandOtherPlayerName commandOtherPlayerName)
	{
		String otherPlayerName = commandOtherPlayerName.getOtherPlayerName();
		int tableNum = commandOtherPlayerName.getTableNum();
		
		serverInfo.getTableList().getTable(tableNum).setOtherPlayerUsername(otherPlayerName);
		serverInfo.getTableList().refreshObservers();
	}

	public void joinTable(CommandJoinTable cmdJoinTable) {
		Table table = serverInfo.getTableList().getTable(cmdJoinTable.getTableNum());
		String username = cmdJoinTable.getUsername();
		table.addPlayer(new User(username));
		serverInfo.getTableList().refreshObservers();
	}
	
	public String getUsername(){
		return username;
	}
	
	public void setUsername(String value) {
		username = value;
	}

	public TableList getTableList() {
		return serverInfo.getTableList();
	}

	public UserList getUserList() {
		return serverInfo.getUserList();
	}
	
	public ServerInfo getGameInfo() {
		return serverInfo;
	}

	@Override
	public void handleDisconnection()
	{
		String message = "The server got disconnected !" + Settings.NEW_LINE +
						 "If you are already in the middle of a game, you will be able to continue," + Settings.NEW_LINE +
						 "but the result will not be added to your statistics." + Settings.NEW_LINE +
						 "Also, you will now return to the logon screen. Please try to logon again shortly." + Settings.NEW_LINE + Settings.NEW_LINE +
						 "Sorry for the inconvinience.";
		JOptionPane.showMessageDialog(getClientFrame(), message, "Server Disconnected", JOptionPane.ERROR_MESSAGE);
		
		getClientFrame().switchToLogon();
	}
	
	public ClientFrame getClientFrame(){
		return clientFrame;
	}

	@Override
	public Table getTable() {
		return null;
	}

	@Override
	public void broadcast(ITransmittable element) {
	}
}
	